<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        body {
            background: black;
        }
        canvas {
            position: absolute;
            top: 50%;
            left: 50%;
        }
        button {
            position: fixed;
            bottom: 30px;
            right: 20px;
        }
        #docs {
            position: absolute;
            color: #aaa;
            text-decoration: none;
            bottom: 10px;
            right: 20px;
            font: 10px Arial;
        }
        #docs:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>

</body>
</html>

<script>
    (function() {
        var DeJongAttractor, N, Sketch, canvas, context, cos, density, iterations, limit, log, max, mouseX, mouseY, round, sensitivity, sin, size, start,
                __bind = function(fn, me) {
                    return function() {
                        return fn.apply(me, arguments);
                    };
                };

        sensitivity = 0.02;

        iterations = 8000;

        density = 2;

        start = 0;

        limit = 200;

        size = 700;

        N = size * window.devicePixelRatio || 1;

        canvas = document.createElement('canvas');

        canvas.width = canvas.height = N;

        canvas.style.width = canvas.style.height = size + 'px';

        canvas.style.marginTop = canvas.style.marginLeft = '-' + (size / 2) + 'px';

        document.body.appendChild(canvas);

        context = canvas.getContext('2d');

        round = Math.round, log = Math.log, max = Math.max, sin = Math.sin, cos = Math.cos;

        mouseX = mouseY = start;

        Sketch = (function() {

            function Sketch() {
                this.permalink = __bind(this.permalink, this);

                this.resume = __bind(this.resume, this);

                this.record = __bind(this.record, this);

                this.pause = __bind(this.pause, this);

                this.tick = __bind(this.tick, this);
                this.steps = 0;
                this.stopped = false;
                document.addEventListener('mousedown', this.pause, false);
                document.addEventListener('mousemove', this.record, false);
                document.addEventListener('mouseup', this.resume, false);
                this.initialSeed();
                this.attractor = new DeJongAttractor;
                this.loop();
            }

            Sketch.prototype.loop = function() {
                return this.interval = setInterval(this.tick, 0);
            };

            Sketch.prototype.stopLoop = function() {
                return this.interval = clearInterval(this.interval);
            };

            Sketch.prototype.tick = function() {
                if (this.stopped) {
                    return this.attractor.reseed();
                }
                this.steps += 1;
                this.attractor.plot(5);
                if (this.steps > limit) {
                    return this.stopLoop();
                }
            };

            Sketch.prototype.initialSeed = function() {
                var hash, num, _ref;
                if (hash = window.location.hash.replace('#', '')) {
                    return _ref = (function() {
                        var _i, _len, _ref, _results;
                        _ref = hash.split(',');
                        _results = [];
                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                            num = _ref[_i];
                            _results.push(parseInt(num, 10));
                        }
                        return _results;
                    })(), mouseX = _ref[0], mouseY = _ref[1], _ref;
                }
            };

            Sketch.prototype.pause = function(e) {
                e.preventDefault();
                this.stopped = true;
                if (!this.interval) {
                    return this.loop();
                }
            };

            Sketch.prototype.record = function(e) {
                if (this.stopped) {
                    mouseX = e.pageX - canvas.offsetLeft;
                    return mouseY = e.pageY - canvas.offsetTop;
                }
            };

            Sketch.prototype.resume = function() {
                this.stopped = false;
                return this.steps = 0;
            };

            Sketch.prototype.permalink = function(e) {
                e.stopPropagation();
                return window.location.hash = mouseX + ',' + mouseY;
            };

            return Sketch;

        })();

        DeJongAttractor = (function() {

            function DeJongAttractor() {
                this.reseed();
            }

            DeJongAttractor.prototype.clear = function() {
                var i, j;
                this.image = context.createImageData(N, N);
                this.density = (function() {
                    var _i, _results;
                    _results = [];
                    for (j = _i = 0; 0 <= N ? _i < N : _i > N; j = 0 <= N ? ++_i : --_i) {
                        _results.push((function() {
                            var _j, _results1;
                            _results1 = [];
                            for (i = _j = 0; 0 <= N ? _j < N : _j > N; i = 0 <= N ? ++_j : --_j) {
                                _results1.push(0);
                            }
                            return _results1;
                        })());
                    }
                    return _results;
                })();
                return this.maxDensity = 0;
            };

            DeJongAttractor.prototype.seed = function() {
                var _ref;
                this.xSeed = (mouseX * 2 / N - 1) * sensitivity;
                this.ySeed = (mouseY * 2 / N - 1) * sensitivity;
                return _ref = [N / 2, N / 2], this.x = _ref[0], this.y = _ref[1], _ref;
            };

            DeJongAttractor.prototype.populate = function(samples) {
                var i, row, x, y, _i, _ref, _ref1;
                for (i = _i = 0, _ref = samples * iterations; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
                    x = ((sin(this.xSeed * this.y) - cos(this.ySeed * this.x)) * N * 0.2) + N / 2;
                    y = ((sin(-this.xSeed * this.x) - cos(-this.ySeed * this.y)) * N * 0.2) + N / 2;
                    this.density[round(x)][round(y)] += density;
                    _ref1 = [x, y], this.x = _ref1[0], this.y = _ref1[1];
                }
                return this.maxDensity = log(max.apply(Math, (function() {
                    var _j, _len, _ref2, _results;
                    _ref2 = this.density;
                    _results = [];
                    for (_j = 0, _len = _ref2.length; _j < _len; _j++) {
                        row = _ref2[_j];
                        _results.push(max.apply(Math, row));
                    }
                    return _results;
                }).call(this)));
            };

            DeJongAttractor.prototype.reseed = function() {
                this.clear();
                this.seed();
                return this.plot(1);
            };

            DeJongAttractor.prototype.softLight = function(a, b) {
                return ((a * b) >> 7) + ((a * a) >> 8) - ((a * a * b) >> 15);
            };

            DeJongAttractor.prototype.plot = function(samples) {
                var color, current, data, dens, i, idx, j, light, _i, _j;
                this.populate(samples);
                data = this.image.data;
                for (i = _i = 0; 0 <= N ? _i < N : _i > N; i = 0 <= N ? ++_i : --_i) {
                    for (j = _j = 0; 0 <= N ? _j < N : _j > N; j = 0 <= N ? ++_j : --_j) {
                        dens = this.density[i][j];
                        idx = (i * N + j) * 4;
                        data[idx + 3] = 255;
                        if (dens <= 0) {
                            continue;
                        }
                        light = log(dens) / this.maxDensity * 255;
                        current = data[idx];
                        color = this.softLight(light, current);
                        data[idx] = data[idx + 1] = data[idx + 2] = color;
                    }
                }
                return context.putImageData(this.image, 0, 0);
            };

            return DeJongAttractor;

        })();

        new Sketch();

    }).call(this);
</script>